﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Security.Cryptography;

namespace SecureConnection.Encryption
{
    public unsafe class DynamicKeyEncryption
    {       
        public DynamicKeyEncryption(byte[] key)
        {
            this.org_key = new byte[key.Length];
            this.decrypt_key = new byte[key.Length];
            this.encrypt_key = new byte[key.Length];

            for (int i = 0; i < key.Length; i++)
            {
                org_key[i] = key[i];

                encrypt_key[i] = key[i];
                decrypt_key[i] = key[i];
            }
        }

        public byte[] Encrypt(byte[] data)
        {
            lock (sync_enc)
            {
                Encrypt(ref data, 0, data.Length);
                return data;
            }
        }
        public void Encrypt(ref byte[] data, int offset, int length)
        {
            lock (sync_enc)
            {
                fixed (byte* ptr = data)
                {
                    length = offset + length;

                    byte tmp = 0;
                    int modTmp = 0;
                    for (int i = offset; i < length; i++)
                    {
                        modTmp = (i) % encrypt_key.Length;
                        tmp = ptr[i];
                        ptr[i] = (byte)((ptr[i] + encrypt_key[modTmp]) % 256);
                        encrypt_key[modTmp] = (byte)((encrypt_key[modTmp] + tmp) % 256);
                    }
                }
            }
        }

        public byte[] Decrypt(byte[] data)
        {
            lock (sync_dec)
            {
                Decrypt(ref data, 0, data.Length);
                return data;
            }
        }
        public void Decrypt(ref byte[] data, int offset, int length, int keyOffset = 0)
        {
            lock (sync_dec)
            {
                fixed (byte* ptr = data)
                {
                    length = offset + length;

                    short tmp = 0;
                    int modTmp = 0;
                    for (int i = offset; i < length; i++)
                    {
                        modTmp = (i + keyOffset) % decrypt_key.Length;
                        tmp = (short)(ptr[i] - decrypt_key[modTmp]);
                        ptr[i] = (byte)(tmp < 0 ? tmp + 256 : tmp);
                        decrypt_key[modTmp] = (byte)((decrypt_key[modTmp] + ptr[i]) % 256);
                    }
                }
            }
        }

        byte[] decrypt_key;
        byte[] encrypt_key;

        public byte[] org_key;
        object sync_enc = new object();
        object sync_dec = new object();
    }
}
